#ifndef SELECTION_HEADER_VIZMF_PROJECT
#define SELECTION_HEADER_VIZMF_PROJECT

#include <string>
#include <vector>
//#include <deque>
#include <iterator>
#include <set>
#include <algorithm>
#include <functional>

#include <src/core/workspace/molecule/molecule.h>   // includes also atom.h and bond.h as well as proper bond specification given in Molecule
#include <src/core/workspace/lattice.h>
#include <src/core/workspace/workspace.h>


/*
  Wrapper iterator that remembers a container whose object it points
*/
template <typename ObjectIterator, typename ContainerType>
class container_iterator : public ObjectIterator {
public:
  typedef ContainerType cont_type;
  typedef ObjectIterator iterator;
  typedef typename ObjectIterator::pointer pointer;

public:
  container_iterator() : ObjectIterator (iterator()) {}
  container_iterator(iterator iter, const cont_type& cont) : ObjectIterator(iter), Cont_(cont) {}

  const cont_type& container() const {return Cont_;}  // the main purpose of its existence :)

  const pointer& base() const { return ObjectIterator::base();}

protected:
  const cont_type& Cont_;
};


template< typename ObjectIterator, typename ContainerType>
inline bool
operator< (const container_iterator<ObjectIterator, ContainerType>& lhs, const container_iterator<ObjectIterator, ContainerType>& rhs)
{
  if(lhs.container().number() < rhs.container().number())
    return true;
  else if(rhs.container().number() < lhs.container().number())
    return false;
  else return lhs.base() < rhs.base();  // the same container
}

template<>
inline bool
operator< (const container_iterator<Molecule::atom_iterator, Molecule>& lhs, const container_iterator<Molecule::atom_iterator, Molecule>& rhs)
{
  if(lhs.container().number() < rhs.container().number())
    return true;
  else if(rhs.container().number() < lhs.container().number())
    return false;
  else return lhs->number() < rhs->number();  // the same container
}

template< typename ObjectIterator, typename ContainerType>
inline bool
operator== (const container_iterator<ObjectIterator, ContainerType>& lhs, const container_iterator<ObjectIterator, ContainerType>& rhs)
{
  return (lhs.container().number() == rhs.container().number() ) && lhs.base() == rhs.bases();
}

template< typename ObjectIterator, typename ContainerType>
inline bool
operator!= (const container_iterator<ObjectIterator, ContainerType>& lhs, const container_iterator<ObjectIterator, ContainerType>& rhs)
{
  return (lhs.container().number() != rhs.container().number() ) || lhs->number() != rhs->number();
}


/*
  Object iterator to iterate a container of iterators to Object and access Object directly
*/
template < typename ObjectInnerIterator >
class object_iterator {
public:
  typedef ObjectInnerIterator iterator;
  typedef typename iterator::value_type inner_iterator;  // assuming value_type is iterator. Concept check is needed in future...
  typedef typename inner_iterator::pointer pointer;
  typedef typename inner_iterator::reference reference;
//  typedef typename iterator::pointer pointer;
//  typedef typename iterator::reference reference;

public:
  object_iterator(iterator iter = iterator()) : iter_(iter) {}

  reference operator*() const { return *(*iter_); }  // double dereference
  pointer operator->() const { return (*iter_).operator->();}
//  pointer operator->() const { return (ObjectInnerIterator::operator->()).operator->();}
//  pointer operator->() const { return ObjectInnerIterator::operator->();}

  object_iterator& operator++(){
    ++iter_;
    return *this;
  }

  object_iterator operator++(int){
    return object_iterator(iter_++);
  }

  const pointer& base() const {return (*iter_).base();}

protected:
  iterator iter_;
};


template< typename ObjectInnerIterator>
inline bool
operator==(const object_iterator<ObjectInnerIterator>& lhs, const object_iterator<ObjectInnerIterator>& rhs)
//{ return (*lhs.base()).base() == (*rhs.base()).base();}
{ return lhs.base() == rhs.base();}

template< typename ObjectInnerIterator>
inline bool
operator!=(const object_iterator<ObjectInnerIterator>& lhs, const object_iterator<ObjectInnerIterator>& rhs)
{ return lhs.base() != rhs.base();}

/*
	Range interator
*/
template <typename MapType>
class RangeIterator {
public:
	typedef MapType map_type;
	typedef map_type::iterator map_iterator;
	typedef map_type::key_type r_iterator;
	
	RangeIterator() {};
	RangeIterator(map_type& map) : 
		_cur_map_iterator(map.begin()),	_end_map_iterator (map.end()) {};
	
	RangeIterator& operator++ () {
		++_cur_iterator;
		// some leap magic here
		if( _cur_iterator == _cur_map_iterator.second ){
			++_cur_map_iterator;
			if(_cur_map_iterator == _end_map_iterator)
				_cur_iterator = r_iterator();
			else
				_cur_iterator = _cur_map_iterator.first;
		}
		return *this;
	}
	
	r_iterator operator* () { return _cur_iterator; }
	
protected:
	r_iterator _cur_iterator;
	map_iterator _cur_map_iterator, _end_map_iterator;
};


/*
    VizMF UniformSelection class

    A selection composed of uniform iterators to (uniform) objects from one or more (uniform :) containers.
    Ob_iterator returned by obegin()/oend() pair make possible traversal of a selection as a collection
    of objects.
    Internally iterators are stored with reference on containers associated with them (as container_iterator)

	TODO: starting making inetrval storqage as of 22.10.20121
	tasks:
	1. interval storage
	2. merge intervals
	3. construct intervals from a straightforward set
*/

template<typename ContainerIterator>
class UniformSelection {
public:
  typedef ContainerIterator iterator;
  typedef typename iterator::iterator inner_iterator;
  typedef typename iterator::cont_type cont_type;
//  typedef const iterator const_iterator;    // not sure it is that easy. now sure - not that easy
  typedef UniformSelection<ContainerIterator> Self_;

protected:
  typedef std::map<iterator, iterator> int_container_type;
//  typedef std::set<iterator> int_container_type;
//  typedef std::vector<iterator> int_container_type;
  typedef typename int_container_type::iterator int_cont_iterator;

  // auxiliary function-object to generate contaiter_iterator's
  // todo: possibly it should be defined outside
  struct conv : public std::unary_function<inner_iterator, iterator> {
    conv(const cont_type& c) : c_(c) {}
    iterator operator() (inner_iterator i) const { return iterator(i, c_); }
  protected:
    const cont_type& c_;
  };

public:
  typedef typename int_container_type::size_type size_type;
  typedef object_iterator<int_cont_iterator> ob_iterator;

public:
  UniformSelection(){}

  // constructing from a range of container_iterator <inner_iterator, Cont>
//  UniformSelection(iterator beg, iterator end) : sel_(beg, end) {}
  UniformSelection(iterator beg, iterator end) { sel_[beg] = end; }

  // take a pair of inner_iterators and wrap all the range into container_iterators
  UniformSelection(inner_iterator beg, inner_iterator end, const cont_type& Cont) {
    conv c(Cont);
    sel_[c(beg)] = c(end);
  }

  ob_iterator obegin(){ return ob_iterator (sel_.begin()); }
  ob_iterator oend(){ return ob_iterator (sel_.end()); }
//  iterator begin(){ return sel_.begin(); }
//  iterator end(){ return sel_.end(); }
//  const_iterator begin() const { return const_iterator (static_cast<Base_>(this) ); }
//  const_iterator end() const { return const_iterator (); }

public:
    // a couple of convenience functions
    bool empty() const { return sel_.empty(); }
	size_type size() const { return sel_.size(); } // TODO: either dynamically calculate size or keep track of it while modifying sel_

    // set union
    //    UniformSelection& operator+ (const Self_& us) // shulga: be carefull!! don't return reference to local object
    UniformSelection operator+ (const Self_& us){
        Self_ res;
        std::set_union(sel_.begin(), sel_.end(), us.sel_.begin(), us.sel_.end(), std::inserter(res.sel_, res.sel_.begin()) );
        return res;
    }

    // set intersection
    UniformSelection operator* (const UniformSelection& us){
        Self_ res;
        std::set_intersection(sel_.begin(), sel_.end(), us.sel_.begin(), us.sel_.end(), res.sel_.begin());
        return res;
    }

    // set difference
    UniformSelection operator- (const UniformSelection& us){
        Self_ res;
        std::set_difference(sel_.begin(), sel_.end(), us.sel_.begin(), us.sel_.end(), res.sel_.begin());
        return res;
    }

    // check for inclusion
    bool includes(const Self_ & sel){
      return std::includes(sel_.begin(), sel_.end(), sel.sel_.begin(), sel.sel_.end());
    }

    // be carefull!! this function most most probably should be overloaded in concrete classes
    // to substitute base begin()/end() pair to real pair, e.g. atoms_begin()/atoms_end()
    Self_& select_all(const cont_type& Cont){
      return *this = Self_(Cont.begin(), Cont.end(), Cont);
    }

    Self_& select_all(inner_iterator beg, inner_iterator end, const cont_type& Cont) {
      return *this = Self_(beg, end, Cont);
    }

    Self_& insert(inner_iterator beg, inner_iterator end, const cont_type& Cont) {
      return *this = *this + Self_(beg, end, Cont);
    }
    
    // make a uniform union of iterator ranges
    void normalize() {
    	RangeIterator<int_container_type> ri(sel_), re();
    	// 1. first, make hard work through a set
    	std::set<iterator> s(ri, re);
    	// 2. regroup intervals
    	typedef std::set<iterator>::iterator set_iterator;
    	int_container_type new_map;
    	set_iterator b = s.begin();
    	iterator cur_begin;
    	for(;b != s.end();){
    		iterator cur_iter = cur_begin = *b;
    		for( ;++b != s.end(); ){
    			iterator prev = cur_iter;
    			if(*b != ++cur_iter){
    				new_map[cur_begin] = prev;
    				break;
    				//TODO: check all logics
    			}
    		}
    	}
    	
    	sel_.swap(new_map);
    }

protected:
    int_container_type sel_;
};


typedef container_iterator<Molecule::atom_iterator, Molecule> AtomContIterator;
typedef container_iterator<Molecule::bond_iterator, Molecule> BondContIterator;
typedef container_iterator<Lattice::points_const_iterator, Lattice> GridContIterator;

typedef UniformSelection<AtomContIterator> atom_selection;
typedef UniformSelection<BondContIterator> bond_selection;
typedef UniformSelection<GridContIterator> node_selection;


/*
  VizMF Selection class

  As for now (27.10.2011), Selection is a union of a predefined set of selections
  Made template to resolve cyclic dependence of Selection and WorkSpace.
*/

template <typename Workspace>
class Selection {
public:
  typedef ::atom_selection atom_sel_type;
  typedef ::bond_selection bond_sel_type;
  typedef ::node_selection node_sel_type;

  typedef atom_sel_type::ob_iterator atom_leaf_iterator;
  typedef bond_sel_type::ob_iterator bond_leaf_iterator;
  typedef node_sel_type::ob_iterator node_leaf_iterator;

  typedef Workspace workspace;

public:
  // Selection() {} // possibly does not have much sense
  Selection(atom_sel_type atom_sel, bond_sel_type bond_sel, node_sel_type node_sel)
    : atom_sel_(atom_sel),  bond_sel_(bond_sel), node_sel_(node_sel) {}
//  Selection(const Selection& s) {}    // not needed in current implementation

  /*
    Selection should span objects in the workspace
    Select all should select all objects in the workspace
  */
  Selection(workspace* wsp, std::string mask = "*:*:*") : workspace_(wsp) {
    if(workspace_)
      select_all();   // now select everything appropriate from the workspace

    // the mask is skipped for a while :)
  };

  atom_leaf_iterator atoms_begin(){return atom_sel_.obegin();}
  atom_leaf_iterator atoms_end(){return atom_sel_.oend();}

  bond_leaf_iterator bonds_begin(){return bond_sel_.obegin();}
  bond_leaf_iterator bonds_end(){return bond_sel_.oend();}

  node_leaf_iterator nodes_begin(){return node_sel_.obegin();}
  node_leaf_iterator nodes_end(){return node_sel_.oend();}


protected:
  // AtomSelection
  // BondSelection
  // LatticeNodeSelection
  atom_sel_type atom_sel_;
  bond_sel_type bond_sel_;
  node_sel_type node_sel_;
  workspace * workspace_;

public:
  // set union
  Selection operator+ (const Selection& s) const {
      Selection res(*this);
      res.atom_sel_ = res.atom_sel_ + s.atom_sel_;
      res.bond_sel_ = res.bond_sel_ + s.bond_sel_;
      res.node_sel_ = res.node_sel_ + s.node_sel_;
      return res;
  }

  // set intersection
  Selection operator* (const Selection& s) const {
      Selection res(*this);
      res.atom_sel_ = res.atom_sel_ * s.atom_sel_;
      res.bond_sel_ = res.bond_sel_ * s.bond_sel_;
      res.node_sel_ = res.node_sel_ * s.node_sel_;
      return res;
  }

  // set difference
  Selection operator- (const Selection& s){
      Selection res(*this);
      res.atom_sel_ = res.atom_sel_ - s.atom_sel_;
      res.bond_sel_ = res.bond_sel_ - s.bond_sel_;
      res.node_sel_ = res.node_sel_ - s.node_sel_;
      return res;
  }

  // check for inclusion
  bool includes (const Selection& s) const {
      return atom_sel_.includes(s.atom_sel_) &&
              bond_sel_.includes(s.bond_sel_) &&
              node_sel_.includes(s.node_sel_);
  }

  // select all
  Selection& select_all(){
      // first, procede with molecules' atoms and bonds
      typedef typename workspace::molecule_iterator_type MI;
      for(MI i = workspace_ -> moleculesBegin(), e = workspace_ -> moleculesEnd(); i!=e; i++){
          atom_sel_ = atom_sel_ + atom_sel_type().select_all(i->atomsBegin(), i->atomsEnd(), *i);
          bond_sel_ = bond_sel_ + bond_sel_type().select_all(i->bondsBegin(), i->bondsEnd(), *i);
/*          atom_sel_.select_all(i->atomsBegin(), i->atomsEnd(), *i);
            atom_sel_ = atom_sel_type().select_all(i->atomsBegin(), i->atomsEnd(), *i) +
                atom_sel_type().select_all(i->atomsBegin(), i->atomsEnd(), *i);
          break;
*/
      }

      // then. procede with lattices' modes
      typedef typename workspace::lattice_iterator_type LI;
      for(LI i = workspace_ -> latticesBegin(), e = workspace_ -> latticesEnd(); i!=e; i++){
          node_sel_ = node_sel_ + node_sel_type().select_all(i->pointsBegin(), i->pointsEnd(), *i);
      }

      return *this;
  }

  // invert selection
  Selection invert(){
      Selection res(workspace_);
      res.select_all();
      res = res - *this;
      return res;
  }

  // convenience functions
  // conversion constuctors
  Selection(const atom_sel_type& as){
    atom_sel_ = as;
  }
  Selection(const bond_sel_type& bs){
    bond_sel_ = bs;
  }
  Selection(const node_sel_type& ns){
    node_sel_ = ns;
  }

  const atom_sel_type atomSelection() const { return atom_sel_;}
  const bond_sel_type bondSelection() const { return bond_sel_;}
  const node_sel_type nodeSelection() const { return node_sel_;}

};


/*
todo:
======
1) "select something" functionality, e.g. by type, by SMARTS, etc.
*/

#endif // SELECTION_HEADER_VIZMF_PROJECT


